package com.chenl.Other;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisHashCommands;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service

public class RedisService2 {
    @Resource(name = "MyRedisTemplate")
    private RedisTemplate redisTemplate;






    //-----------------------------STRING-----------------------------
    public boolean set(String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = this.redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public Object get(String key) {
        Object result = null;
        ValueOperations<Serializable, Object> operations = this.redisTemplate.opsForValue();
        result = operations.get(key);
        return result;
    }

    public boolean setUnit(String key, Object value, Long expireTime, TimeUnit unit) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = this.redisTemplate.opsForValue();
            operations.set(key, value, expireTime.longValue(), unit);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public long incr(String key) {
        return redisTemplate.opsForValue().increment(key, 1);
    }

//    public long decr(String key) {
//        return redisTemplate.opsForValue().decrement(key, 1);
//    }

    public Boolean isMember(String key, Object value) {
        SetOperations<String, Object> set = this.redisTemplate.opsForSet();
        return set.isMember(key, value);
    }

    public boolean remove(String key) {
        Boolean b = false;
        b = this.redisTemplate.delete(key);
        return b;
    }

    /**
     * String类型 管道批处理,大批量数据时效率高
     * @param map
     */
    public void setKeysByPipelineByString(Map<String,Object> map) {
        try {
            RedisSerializer keySerializer = redisTemplate.getKeySerializer();
            RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
            this.redisTemplate.executePipelined(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                    for (Map.Entry<String, Object> entry : map.entrySet()) {
                        redisConnection.stringCommands().set(keySerializer.serialize(entry.getKey()), valueSerializer.serialize(entry.getValue()));
                    }
                    return null;
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 管道批处理,批量删除指定的键值对。
     *
     * @param keys 要删除的键的集合
     */
    public void deleteKeysByPipeline(Set<String> keys) {
        try {
            // 使用 executePipelined 方法执行管道操作
            this.redisTemplate.executePipelined(new RedisCallback<Void>() {
                @Override
                public Void doInRedis(RedisConnection connection) throws DataAccessException {
                    RedisSerializer<String> keySerializer = redisTemplate.getKeySerializer();
                    for (String key : keys) {
                        connection.del(keySerializer.serialize(key));
                    }
                    // 返回 null，因为我们不需要返回任何结果
                    return null;
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //-----------------------------LIST-----------------------------
    public void lPush(String k, Object v) {
        ListOperations<String, Object> list = this.redisTemplate.opsForList();
        list.leftPush(k, v);
    }

    public void rPush(String k, Object v) {
        ListOperations<String, Object> list = this.redisTemplate.opsForList();
        list.rightPush(k, v);
    }

    public Object RPop(String k) {
        ListOperations<String, Object> list = this.redisTemplate.opsForList();
        return list.rightPop(k);
    }

    public Object lPop(String k) {
        ListOperations<String, Object> list = this.redisTemplate.opsForList();
        return list.leftPop(k);
    }

    public Long listSize(String key) {
        return redisTemplate.opsForList().size(key);
    }

    //-----------------------------HASH-----------------------------
    public void hmSet(String key, Object hashKey, Object value) {
        HashOperations<String, Object, Object> hash = this.redisTemplate.opsForHash();
        hash.put(key, hashKey, value );
    }

    public Object hmGet(String key, Object hashKey) {
        HashOperations<String, Object, Object> hash = this.redisTemplate.opsForHash();
        return hash.get(key, hashKey);
    }

    /**
     * 获取一个hash类型中的所有entries
     * @param key
     * @return
     */
    public Map<Object, Object> hmGetEntries(String key) {
        HashOperations<String, Object, Object> hash = this.redisTemplate.opsForHash();
        return hash.entries(key);
    }

    /**
     * 通过keys批量获取values (如果是要获取所有的,则hmGetEntries()更快一点)
     * @param key
     * @param keys
     * @return
     */
    public List<Object>  hmGetMulti(String key,Collection<Object> keys) {
        HashOperations<String, Object, Object> hash = this.redisTemplate.opsForHash();
        return hash.multiGet(key,keys);
    }

    public Object hmIncr(String key, Object hashKey,long incrCount) {
        HashOperations<String, Object, Object> hash = this.redisTemplate.opsForHash();
        return hash.increment(key, hashKey,incrCount);
    }

    public Map<Object, Object> hmEntry(String key) {
        HashOperations<String, Object, Object> hash = this.redisTemplate.opsForHash();
        return hash.entries(key);
    }

    public Set<Object> hmKeys(String key) {
        HashOperations<String, Object, Object> hash = this.redisTemplate.opsForHash();
        return hash.keys(key);
    }

    public Integer hmSize(String key) {
        Integer size = null;
        HashOperations<String, Object, Object> hash = this.redisTemplate.opsForHash();
        try {
            size = hash.keys(key).size();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return size;
    }

    public void hmDel(String masterKey, Object... hashKey) {
        HashOperations<String, Object, Object> hash = this.redisTemplate.opsForHash();
        hash.delete(masterKey, hashKey);
    }

    /**
     * Hash类型 管道批处理Set,大批量数据时效率高
     * @param map
     */
    public void hmSetKeysByPipeline(Map<String,Map<String,Object>> map) {
        try {
            RedisSerializer keySerializer = redisTemplate.getKeySerializer();
            RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
            this.redisTemplate.executePipelined(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                    for (Map.Entry<String, Map<String,Object>> entry : map.entrySet()) {
                        Map<String, Object> value = entry.getValue();
                        Map<byte[], byte[]> hMap=new HashMap<>();
                        value.forEach((k,v)->hMap.put(valueSerializer.serialize(k),valueSerializer.serialize(v)));
                        RedisHashCommands redisHashCommands = redisConnection.hashCommands();
                        redisHashCommands.hMSet(keySerializer.serialize(entry.getKey()), hMap);
                    }
                    return null;
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //-----------------------------SET-----------------------------
    public Long setSet(String key, Object value) {
        long a = 0;
        try {
            Long add = this.redisTemplate.boundSetOps(key).add(value);
            a = add;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return a;
    }

    public Boolean setIsmember(String key, Object value) {//根据key和value在一个Set中查询是否存在
        Boolean member = false;
        try {
            member = this.redisTemplate.boundSetOps(key).isMember(value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return member;
    }

    //-----------------------------ZSET-----------------------------
    public void zSetAdd(String key, Object value, double scoure) {
        ZSetOperations<String, Object> zset = this.redisTemplate.opsForZSet();
        zset.add(key, value, scoure);
    }

    public Set zGetDesc(String key) {//从低到高获取zset集合中的所有元素
        Set zsetVslue = redisTemplate.opsForZSet().range(key, 0, -1);
        return zsetVslue;
    }


    //------------------------------通用---------------------------------

    /**
     * 异步删除
     * @param keys
     */

    public void unlink(Collection<String> keys){
        redisTemplate.unlink(keys);
    }


    /**
     * 根据key 获取过期时间
     *
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    public boolean expire(String key, Long expireTime, TimeUnit unit) {
        boolean result = false;
        try {
            result = this.redisTemplate.expire(key, expireTime.longValue(), unit);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public Set<String> sCan(String matchKey) {//根据key进行模糊查询,返回所有在匹配规则内的key
        Set<String> keys = (Set<String>) redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> keysTmp = new HashSet<>();
            Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(matchKey).count(10000).build());
            while (cursor.hasNext()) {
                keysTmp.add(new String(cursor.next()));
            }
            return keysTmp;
        });
        return keys;
    }
    /**
     * scan 实现
     *
     * @param pattern       表达式，如：abc*，找出所有以abc开始的键
     */
    public  Set<String> scan(String pattern) {
        Set<String> keysTmp = new HashSet<>();
        redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            try (Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder()
                    .match(pattern)
                    .count(10000).build())) {
                while (cursor.hasNext()) {
                    keysTmp.add(new String(cursor.next(), "Utf-8"));
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return keysTmp;
        });
        return keysTmp;
    };

    /**
     * 根据前缀获取所有的key
     * 例如：pro_*
     */
    public Set<String> getListKey(String prefix) {
        Set<String> keys = redisTemplate.keys(prefix.concat("*"));
        return keys;
    }

    /**
     * 根据value模糊查询出所有Key
     * @param value
     * @return
     */
    public List<String> selectKeysByValue(String value){
        List<String>keyList=new ArrayList<>();
        Set<String> keys = sCan("*");
        keyList.addAll(keys);
        List<String>list=new ArrayList<>();
        List values = redisTemplate.opsForValue().multiGet(keys);
        for (int i = 0; i < values.size(); i++) {
            String v = values.get(i).toString();
            if (v.contains(value)){
                list.add(keyList.get(i));
            }
        }
        return list;
    }

    /**
     * 设置数据库索引
     *
     * @param dbIndex
     */
    public void selectRedisDataBase(Integer dbIndex) {
        if (dbIndex == null || dbIndex > 15 || dbIndex < 0) {
            dbIndex = 0;
        }
//        LettuceConnectionFactory connectionFactory = (LettuceConnectionFactory) redisTemplate.getConnectionFactory();
//        if (connectionFactory != null && dbIndex != connectionFactory.getDatabase()) {
//            connectionFactory.setDatabase(dbIndex);
//            this.redisTemplate.setConnectionFactory(connectionFactory);
//            connectionFactory.resetConnection();
//        }
        LettuceConnectionFactory jedisConnectionFactory = (LettuceConnectionFactory) redisTemplate
                .getConnectionFactory();
        jedisConnectionFactory.setDatabase(dbIndex);
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        jedisConnectionFactory.afterPropertiesSet();
    }
}